package ai;

import java.util.Iterator;

import settings.Config;
import logic.nodes.TeamNode;
import logic.nodes.collision.CollidableNode;
import logic.nodes.lod.LODNode;
import logic.nodes.lod.blocks.AbstractBlock;
import logic.nodes.lod.blocks.Block;
import logic.nodes.lod.blocks.LeafBlock;
import logic.ships.Ship;
import logic.ships.moveableShip.MovableShip;
import logic.weapons.energyWeapons.EnergyWeapon;

import com.jme.math.FastMath;
import com.jme.math.Ray;
import com.jme.math.Vector3f;

public class AITools {
	
	public static float getMin(float a, float b, float c) {
		if(a < b && a < c) return a;
		if(b < a && b < c) return b;
		return c;
	}
	
	public static float getMax(float a, float b, float c) {
		if(a > b && a > c) return a;
		if(b > a && b > c) return b;
		return c;
	}
	
	public static boolean isLookingTo(Ship ship, CollidableNode targetShip) {
		if(targetShip == null || targetShip.getWorldBound() == null) return false;
        Vector3f direction = targetShip.getLocalTranslation().subtract(ship.getLocalTranslation()).normalize();
        Vector3f frontDir = ship.getFrontDirection();
        float angle = direction.angleBetween(frontDir) * FastMath.RAD_TO_DEG;       
        return angle <= 50f;
	}
	
	public static boolean isVisible(Ship ship, TeamNode target) {
		int visCalcLevel = Config.get().getVisCalc();
		if(visCalcLevel == 0) return true;
		
		return isLookingTo(ship, target);
	}
	
	public static boolean intersects(LeafBlock leafBlock, TeamNode origin, Ray ray, float targetDist) {
		Iterator<LODNode> iter = leafBlock.getLODNodes().iterator();
		while(iter.hasNext()) {
			LODNode node = iter.next();
			if(origin != node && intersects(ray, node, targetDist)) return true;
		}
		return false;
	}
	
	public static boolean intersects(AbstractBlock absBlock, AbstractBlock lastBlock, 
									TeamNode origin, Ray ray, float targetDist) {
		if(tooFar(absBlock.getCenter(), ray, targetDist)) return false;
		
		if(absBlock instanceof Block) {
			Block block = (Block)absBlock;
			
			AbstractBlock child = (lastBlock == block.getBlockA() ? block.getBlockB() : block.getBlockB());
			if(checkChildBlocks(child, ray, origin, targetDist)) return true;
			return intersects(block.getParentBlock(), block, origin, ray, targetDist);
		}
		return intersects((LeafBlock)absBlock, origin, ray, targetDist);
	}
	
	public static boolean checkChildBlocks(AbstractBlock block, Ray ray, TeamNode origin, 
											float targetDist) {
		if(tooFar(block.getCenter(), ray, targetDist)) return false;
		if(block instanceof Block) {
			Block b = (Block)block;
			
			return checkChildBlocks(b.getBlockA(), ray, origin, targetDist) 
				|| checkChildBlocks(b.getBlockB(), ray, origin, targetDist);
		}
		return intersects((LeafBlock)block, origin, ray, targetDist);
	}
	
	private static boolean intersects(Ray ray, LODNode node, float targetDist) {
		if(node.getWorldBound() == null || tooFar(node.getLocalTranslation(), ray, targetDist)) 
			return false;
		return node.getWorldBound().intersects(ray);
	}
	
	public static boolean tooFar(Vector3f loc, Ray ray, float targetDist) {
		return loc.distance(ray.getOrigin()) > targetDist;
	}
	
	public static Vector3f getEnergyFireLocation(EnergyWeapon weapon, TeamNode target) {
		Vector3f targetLoc = target.getLocalTranslation();
		if(target instanceof MovableShip && target.canMove()) {
			MovableShip targetShip = (MovableShip)target;
			Vector3f targetMoveDir = targetShip.getFrontDirection().normalizeLocal();
			
			float targetSpeed = targetShip.getCurrentEngineSpeed();
			float fireSpeed = weapon.getWeaponProperties().getSpeed();
			
			Vector3f toTarget = targetLoc.subtract(weapon.getFireSpotLocation());
			Vector3f tSpeedVector = targetMoveDir.multLocal(targetSpeed).normalizeLocal();
			Vector3f fireSpot = weapon.getFireSpotLocation();
			
			float a = (fireSpeed * fireSpeed) - tSpeedVector.dot(tSpeedVector);
			float b = - 2 * tSpeedVector.dot(targetLoc.subtract(fireSpot));
			float c = - toTarget.dot(toTarget);
			
			float disc = (b * b) - (4 * a * c);
			
			float discRoot =  FastMath.sqrt(disc);
			float twoA = 2 * a;
			float t1 = (b + discRoot) / twoA;
			float t2 = (b - discRoot) / twoA;
			float t = 0f;
			
			if(t1 > t2 && t2 > 0) t = t2;
			else t = t1;
			
			Vector3f aimLoc = targetLoc.add(tSpeedVector.multLocal(t));
			
			return aimLoc;
		}
		return targetLoc;
	}
}